//===--- MathFunctions.swift ----------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import SwiftShims
%from SwiftMathFunctions import *
%from SwiftFloatingPointTypes import all_floating_point_types

/// A type that has elementary functions available.
///
/// An ["elementary function"][elfn] is a function built up from powers, roots,
/// exponentials, logarithms, trigonometric functions (sin, cos, tan) and
/// their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their
/// inverses.
///
/// Conformance to this protocol means that all of these building blocks are
/// available as static functions on the type.
///
/// ```swift
/// let x: Float = 1
/// let y = Float.sin(x) // 0.84147096
/// ```
///
/// Additional operations, such as `atan2(y:x:)`, `hypot(_:_:)` and some
/// special functions, are provided on the Real protocol, which refines both
/// ElementaryFunctions and FloatingPoint.
///
/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
public protocol ElementaryFunctions {

%for func in ElementaryFunctions:

  ${func.comment}
  static func ${func.decl("Self")}
%end

  /// `exp(y log(x))` computed without loss of intermediate precision.
  ///
  /// For real types, if `x` is negative the result is NaN, even if `y` has
  /// an integral value. For complex types, there is a branch cut on the
  /// negative real axis.
  static func pow(_ x: Self, _ y: Self) -> Self

  /// `x` raised to the `n`th power.
  static func pow(_ x: Self, _ n: Int) -> Self

  /// The `n`th root of `x`.
  ///
  /// For real types, if `x` is negative and `n` is even, the result is NaN.
  /// For complex types, there is a branch cut along the negative real axis.
  static func root(_ x: Self, _ n: Int) -> Self
}

%for type in all_floating_point_types():
% if type.bits == 80:
#if (arch(i386) || arch(x86_64)) && !os(Windows)
% end
% Self = type.stdlib_name
extension ${Self}: ElementaryFunctions {
% for func in ElementaryFunctions + RealFunctions:

  @_alwaysEmitIntoClient
  public static func ${func.decl(Self)} {
    return ${func.impl(type)}
  }
% end

  @_alwaysEmitIntoClient
  public static func pow(_ x: ${Self}, _ y: ${Self}) -> ${Self} {
    guard x >= 0 else { return .nan }
    return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, y._value))
  }

  @_alwaysEmitIntoClient
  public static func pow(_ x: ${Self}, _ n: Int) -> ${Self} {
    // TODO: this implementation isn't quite right for n so large that
    // the conversion to `${Self}` rounds. We could also consider using
    // a multiply-chain implementation for small `n`; this would be faster
    // for static `n`, but less accurate on platforms with a good `pow`
    // implementation.
    return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, ${Self}(n)._value))
  }

  @_alwaysEmitIntoClient
  public static func root(_ x: ${Self}, _ n: Int) -> ${Self} {
    guard x >= 0 || n % 2 != 0 else { return .nan }
    // TODO: this implementation isn't quite right for n so large that
    // the conversion to `${Self}` rounds.
    return ${Self}(signOf: x, magnitudeOf: pow(x, 1/${Self}(n)))
  }

  @_alwaysEmitIntoClient
  public static func atan2(y: ${Self}, x: ${Self}) -> ${Self} {
    return _swift_stdlib_atan2${type.cFuncSuffix}(y, x)
  }

#if !os(Windows)
  @_alwaysEmitIntoClient
  public static func logGamma(_ x: ${Self}) -> ${Self} {
    return _swift_stdlib_lgamma${type.cFuncSuffix}(x)
  }

  @_alwaysEmitIntoClient
  public static func signGamma(_ x: ${Self}) -> FloatingPointSign {
    if x >= 0 { return .plus }
    let trunc = x.rounded(.towardZero)
    if x == trunc { return .plus }
    let halfTrunc = trunc/2
    if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
    return .plus
  }
#endif
}
% if type.bits == 80:
#endif
% end
%end

@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
extension SIMD where Scalar: ElementaryFunctions {
% for func in ElementaryFunctions:

  @_alwaysEmitIntoClient
  public static func ${func.decl("Self")} {
    var r = Self()
    for i in r.indices {
      r[i] = Scalar.${func.swiftName}(${func.params(suffix="[i]")})
    }
    return r
  }
% end

  @_alwaysEmitIntoClient
  public static func pow(_ x: Self, _ y: Self) -> Self {
    var r = Self()
    for i in r.indices {
      r[i] = Scalar.pow(x[i], y[i])
    }
    return r
  }

  @_alwaysEmitIntoClient
  public static func pow(_ x: Self, _ n: Int) -> Self {
    var r = Self()
    for i in r.indices {
      r[i] = Scalar.pow(x[i], n)
    }
    return r
  }

  @_alwaysEmitIntoClient
  public static func root(_ x: Self, _ n: Int) -> Self {
    var r = Self()
    for i in r.indices {
      r[i] = Scalar.root(x[i], n)
    }
    return r
  }
}

%for n in [2,3,4,8,16,32,64]:
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
extension SIMD${n}: ElementaryFunctions where Scalar: ElementaryFunctions { }
%end
